feat(fleet): rebuild the dashboard's start and status logic - #162
Merged
Conversation
…ogic The dashboard draws two independent accounts of a node — the status lines a start reports, and the last completed refresh — with no rule for which is fresher or what to do when they contradict. 03718af fixed the line that prompted this; the shape that produced it is untouched. Proposes four changes: a start reports a StartPhase value rather than a string, so supersession is structural; rendering is a pure function of the phase and the current time, so no number can freeze; every observation carries the time it was read, applied monotonically; and a node with an action in flight refreshes on the fast interval whatever its kind. Proposal only — no code changes.
Rewrites the proposal, design and task wording to state what the code does rather than what it wants, knows, remembers or forgets. Also corrects the comments added in 03718af, and a garbled sentence in dashActionProgress that misstated the repaint interval.
The dashboard kept two descriptions of a node — the lines a start wrote and the last refresh round — and compared them for neither recency nor contradiction, which is how a capacity wait came to sit beside a refresh reporting the node running. A start now reports a `fleet.StartPhase` that each transition replaces outright, rendered by one function of the phase and the current time, so a wait counts down, a boot counts up, and a superseded situation cannot be left on screen. `spinloop remote start` renders the same phases, so the tile and the CLI cannot word one situation differently. Every reading carries the time its own call returned, and the board draws one only when it was taken later than what is on screen — which the per-group counters could not do, since they ordered rounds against each other and not against an action completing. A reading that ages past its node's cadence shows its age and reads unknown, and a node with an action in flight is read on the short interval until it settles. A tile's first line is now a coloured header bar, an in-flight verb carries a spinner, and the board has a title bar of its own; the selected panel's border is the brand mint rather than the amber it shared with the attention tier.
What goes on stdout, how an error is worded, when a spinner is drawn and which colour means what were each decided once and since copied from whichever neighbouring command was nearest. That works while there is a neighbour to copy, and gives a reviewer nothing to point at. `cli-ux` records them: one accent used only for the tool's own chrome and never for a state, a stdout a program consumes carrying nothing else, decoration only where there is a terminal to draw it on, errors that name the fix, help as a lowercase imperative phrase, British spelling, what a long operation must keep saying, confirmation before a destructive action, and the full-screen affordances a web page has no equivalent for. It is the CLI counterpart to the site's design-language spec rather than a copy of it. The survey behind it found one command wording that breaks the British spelling rule, corrected here so the spec does not land already violated.
Its nine requirements become `openspec/specs/cli-ux/spec.md`, the capability every command's conventions are now recorded under.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebuilds the fleet dashboard's start reporting, refresh ordering and tile rendering so a node has one description of its state rather than two that can contradict each other, and records the CLI's UX conventions in a spec of their own.
Summary
fleet.StartPhase— what it is doing, when that began, when the next attempt is due — instead of a line of text. Each transition replaces the value outright, so a situation the start has moved on from cannot be left on a tile. This is what the reported defect was:instance no-capacity; retrying in 120ssitting beside a refresh reporting the node running.fleet.RenderPhase(phase, now)is the only place a phase becomes text, and it reads the clock at draw time. A wait counts down, a boot counts up, and no rendered number can be left at a value a transition has passed.spinloop remote startrenders the same phases to stderr, so the tile and the CLI cannot word one situation differently.fleet.NodeResult.At, stamped in the fan-out), and the board draws a reading only when it was taken later than the one on screen. This replaces thefastGen/slowGencounters, which ordered rounds against each other but not against an action completing — so a round issued before a start finished and landing after it would repaint the node's pre-action state.dashNodeView, produces both a panel's lines and its health tier from arguments alone. Every pairing of a start's phase against a reading is now enumerable in a test, including the one that produced this defect.cli-uxspec: one accent used only for the tool's own chrome and never for a state, a stdout a program consumes carrying nothing else, decoration only where there is a terminal to draw it on, errors that name the fix, help as a lowercase imperative phrase, British spelling, what a long operation must keep saying, confirmation before a destructive action, and the full-screen affordances a web page has no equivalent for.cmd/spinloop/palette.goholds the colours and the spinner every surface draws from. The ten braille frames were declared in bothfleet.goanddashboard_model.go, and the five state colours in bothfleet.goand the dashboard's health switch; each is now one definition.Implementation details
Why a value rather than a better-worded line. The quick fix in
03718afcorrected the reported symptom by writing a line at the transition that had been missed. That is correct only while every write site covers every transition, and an omission produces no error and no test failure — the output is a well-formed line carrying an out-of-date value. A phase holds one value per start and each write replaces the previous one, so a superseded situation is not retained by construction.Why timestamps rather than a per-node counter. A counter orders readings equally well, but carries no age, so showing how old a reading is would need a second field beside it.
Atsupplies both the ordering and the age.The read cadence is per node, not per group. The group busy-guard stays — no second round starts over one still in flight — but each node carries its own next-read time, which is how an acted-on cloud environment is polled every 2s while its neighbours stay on 60s. The round's context deadline remains its kind's interval, so shortening the cadence during an action does not shorten what a signed control-plane call is given to answer in.
internal/remoteis untouched.remote.Start's existingprogressandonStatecallbacks already supply everything the phases are built from;fleet.StartPhasesmaps that pair onto the phase stream, and both the dashboard and the CLI drive it.Where the shared palette sits in the history. The rename that consolidated the colours and the spinner happened in place across three files, so the commit that introduces
palette.gois the one carrying the dashboard work rather than the one adding the spec — splitting it the other way would leave the first commit unable to compile, andfleet.go's state colours colliding with the shared set.One behaviour-visible wording change outside the dashboard.
spinloop remote start's stderr lines are reworded, since they now render phases. Nothing parses them — the eval-able exports go to stdout and are unchanged — and the CLI tests that assert on them are updated.